/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package evotingclient.model;

import evotingclient.data.Card;
import evotingclient.data.InitialData;
import evotingclient.data.Vote;
import evotingclient.manager.ClientTool;
import evotingclient.votesending.TCPEchoClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.xml.sax.SAXException;

/**
 *
 * @author Maciek
 */
public class Model {

    private String nick;
    private String password;
    private PersonalData personalData;
    private CertificateManager certificateManager;
    private SSLConnector sslClient;
    private static String path = "Users";
    public static String PersonalDataString = "personalData.txt";
    private ClientTool clientTool;

    private Model(String nick, String password, PersonalData personalData) {
        this.nick = nick;
        this.password = password;
        this.certificateManager = new CertificateManager(nick, password, path);
        this.personalData = personalData;
    }

    public ClientTool getClientTool() {
        return clientTool;
    }

    public void setClientTool(ClientTool clientTool) {
        this.clientTool = clientTool;
    }

    private void writePersonalData() throws FileNotFoundException, IOException, BadPaddingException {
        String getPath = this.getPath(PersonalDataString);
        System.out.println(getPath);
        CypherModel cypher = new CypherModel();
        StringBuilder sb = new StringBuilder();
        sb.append(this.personalData.firstName).append("\n");
        sb.append(this.personalData.lastName).append("\n");
        sb.append(this.personalData.Pesel).append("\n");
        sb.append(this.personalData.randomNumber).append("\n");

        cypher.write(sb.toString(), getPath, password);

    }

    void loadPersonalData() throws FileNotFoundException, IOException, BadPaddingException {
        CypherModel cypher = new CypherModel();
        String getPath = this.getPath(PersonalDataString);
        String result = cypher.load(getPath, password);
        this.personalData = PersonalData.parseString(result);

    }

    private void cleauUp() {
        // tu będzie sprzątanie....
    }
    // haslo będzie pilnowane w keystorze...

    public static Model createNewModel(String nick, String password, PersonalData pd) {
        Model tempModel = new Model(nick, password, pd);
        if (tempModel.nickUsed()) {
            return null;
        } else {
            try {
                if (tempModel.createDirectory()) {
                    System.out.println("Udało się stworzyć odpowiedni folder");
                } else {
                    System.out.println("Nie udało sie stworzyć odpowiedniego folderu");
                    return null;
                }


                tempModel.certificateManager.generateCertificate(pd);
                tempModel.writePersonalData();
                // sprawdzenie że zapisałem dobrze:
                CypherModel cypher = new CypherModel();
                System.out.println(cypher.load(tempModel.getPath(Model.PersonalDataString), password));
                //    tempModel.certificateManager.exportKeyToFile();
            } catch (FileNotFoundException ex) {
                Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
            } catch (BadPaddingException ex) {
                Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                tempModel.cleauUp();
                tempModel = null;
                Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InterruptedException ex) {
                tempModel.cleauUp();
                tempModel = null;
                Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return tempModel;
    }

    public static Model getModel(String nick, String password, PersonalData personalData) {
        Model tempModel = new Model(nick, password, personalData);
        if (!tempModel.nickUsed()) {
            return null;
        } else {
            return tempModel;
        }
    }

    public boolean exportCertificate() throws IOException, InterruptedException {
        int result = this.certificateManager.exportKeyToFile();
        return result == 0;
    }

    boolean nickUsed() {
        File f = new File(path);
        File[] tab = f.listFiles();
        if (tab == null) {
            return false;
        }
        for (File file : tab) {
            if (file.isDirectory() && file.getName().equals(nick)) {
                return true;
            }
        }
        return false;
    }

    boolean createDirectory() {
        File f = new File(getPath(""));
        System.out.println("Będę chciał stworzyć folder : " + f.getAbsolutePath());
        return f.mkdir();
    }

    void downloadPKWCertificate() {
        // narazie zakładam, że on tam jest, będzie trzeba go ściągnąć z jakiejś strony, albo można kazać to zrobić ręcznie 
    }

    public String getPath(String name) {
        StringBuilder mpath = new StringBuilder();
        if (!path.equals("")) {
            mpath.append(path).append(File.separator);
        }

        mpath.append(nick);
        if (!name.equals("")) {
            mpath.append(File.separator);
            mpath.append(name);
        }
        return mpath.toString();
    }

    public void importCertificate() throws IOException, InterruptedException {
        this.certificateManager.trustPKWCertificate();
    }

    public int importSignedCertificate() throws FileNotFoundException, IOException, InterruptedException {
        return this.certificateManager.importSignedCertificate();
    }

    public void register() {
        try {
            sslClient = new SSLConnector(this);
            sslClient.register(this.nick, this.password);
//        } catch (NoSuchAlgorithmException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (KeyStoreException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (FileNotFoundException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (IOException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (CertificateException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (UnrecoverableKeyException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (KeyManagementException ex) {
//            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    PersonalData getPersonalData() throws FileNotFoundException, IOException, BadPaddingException {
        if (personalData != null) {
            return this.personalData;
        } else {// trzeba załadować z pliku
            CypherModel cypher = new CypherModel();
            return PersonalData.parseString(cypher.load(getPath(Model.PersonalDataString), password));
        }
    }

    Certificate getCertificate() {
        try {
            CertificateFactory factory2 = CertificateFactory.getInstance("X.509");
            return factory2.generateCertificate(new FileInputStream(getPath(nick + ".cer")));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (CertificateException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    private byte[] readBytes(File file) throws IOException {
        InputStream is = new FileInputStream(file);

        // Get the size of the file
        long length = file.length();

        // Create the byte array to hold the data
        byte[] bytes = new byte[(int) length];

        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length
                && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }

        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }

        // Close the input stream and return bytes
        is.close();
        return bytes;
    }

    byte[] getSingRequst() {
        try {

            File file = new File(getPath(nick + ".csr"));
            if (file.exists()) {
                System.out.println("Exists such file ");
            } else {

                System.out.println("file does not exists");
                System.out.println(file.getAbsolutePath());
                return null;
            }
            return this.readBytes(file);

        } catch (IOException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public boolean requestSign() throws IOException, InterruptedException {
        int result = this.certificateManager.exportSignReqToFile();
        return result == 0;
    }

    void saveCertificate(byte[] data) throws FileNotFoundException, IOException {
        File file = new File(getPath(nick) + ".pem");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(data);
        fos.flush();
        fos.close();
    }

    void signal(String string) {
        System.out.println(string);
    }

    public void getCards() throws SSLHandshakeException, Exception {
        try {
            sslClient = new SSLConnector(this);
            SSLSocket sslSocket = sslClient.initialSSL(password);
            InitialData idata = sslClient.getInitialData(sslSocket);
            clientTool = new ClientTool(idata, nick);
            clientTool.getVotingData().setStep(0);
            clientTool.blind();
            //sslSocket = sslClient.initialSSL(password);
            System.out.println("po blind a przed jego wyslaniem");
            
            Integer number = sslClient.sendBlind(sslSocket ,clientTool.getVotingData());
            clientTool.getVotingData().setStep(1);
            clientTool.blindN1(number);
            //sslSocket = sslClient.initialSSL(password);
            Card signCard = sslClient.sendVotingData(sslSocket, clientTool.getVotingData());//false jesli karty sa nieprawidlowe          
            clientTool.getVotingData().getSkipCard().mergeCard(signCard);
            clientTool.unblindSignCard();
            clientTool.getVotingData().writeToFile(clientTool.getVotingData().getSkipCard(), certificateManager.getPath("card.pkw"));
            //odczyt zapisanej karty, karta jest podpisana i gotowa do uzycia
//            Card c= clientTool.getVotingData().readFromFile(certificateManager.getPath("card.pkw"));
//            System.out.println("sss");
        } catch (Exception ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
    
    public Card readCard() {
        return clientTool.getVotingData().readFromFile(certificateManager.getPath("card.pkw"));
    }

    public void sendVote(Vote v) {
        try {
            //sent to servere
            TCPEchoClient tc = new TCPEchoClient();
            tc.sendAndReceiveData(new String(v.getSignUnblindVote().toByteArray()).getBytes("UTF-8"));
        } catch (UnknownHostException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(Model.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }
}
